package com.atguigu.juc.threadlocal;

import java.lang.ref.*;
import java.nio.file.Watchable;
import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author yanglin
 * @create 2024-01-30 16:16 星期二
 * description:
 */
public class ReferenceDemo {

    /**
     * 强引用：
     *      当内存不足时，JVM开始垃圾回收，对于强引用对象，就算是出现了OOM也不会对该对象进行回收
     *      强引用是最常见的普通对象引用，只要还有强引用指向一个对象，就能表明对象还“活着”，垃圾收集器不会碰这种对象
     *      在JAVA中，最常见的就是强引用，把一个对象赋给一个引用变量，这个引用变量就是一个强引用
     *      当一个对象被一个强引用变量引用时，它处于可达状态，它是不可能被垃圾回收机制进行回收的
     *      即使该对象以后永远都不会被用到，JVM也不会回收，因此强引用是造成JAVA内存泄露的主要原因之一
     *      对于一个普通对象，如果没有其他的引用关系，只要超过了引用的作用域或者显示的将相应（强）引用赋值为null
     * 软引用：
     *      软引用是相对于强引用弱化了一些的引用，需要用java.lang.ref.SoftReference类来实现，可以让对象豁免一些垃圾收集
     *      对于一个弱引用对象来说：
     *          当系统内存充足时     不会被回收
     *          当系统内存不足时      会被回收
     *      软引用通常用在对内存敏感的程序中，比如高速缓存就有用到软引用，内存够用就保留，不够就回收
     * 弱引用：
     *      弱引用需要用java.lang.ref.WeakReference类来实现，它比软引用生存期更短
     *      对于只有弱引用的对象来说，只要垃圾回收机制一运行，不管JVM内存空间是否足够，都会回收该对象占用的内存
     * 虚引用：
     *      1、虚引用必须和引用队列（ReferenceQueue）联合使用，
     *          虚引用java.lang.ref.PhantomReference类来实现，与其他集中引用都不同，虚引用并不会决定对象的生命周期，
     *          如果一个对象仅持有虚引用，那么他就和没有任何引用一样，在任何时候都可以被垃圾回收器回收，它不能单独使用也不能
     *          通过它访问对象，虚引用必须和应用队列（ReferenceQueue）联合使用
     *      2、PhantomReference的get方法总是返回null
     *          虚引用的主要作用是跟踪对象被垃圾回收的状态，仅仅提供了一种对象被finalize以后，做某些事情的通知机制
     *          PhantomReference的get方法总是返回null,因此无法访问对应的引用对象
     *     3、处理监控通知使用：
     *          换句话说，设置虚引用关联对象的唯一目的，就是这个对象别收集器回收的时候收到一个系统通知或者后续添加进一步的处理，
     *          用来实现比finalize机制更加灵活的操作
     */

    public static void main(String[] args) {
        
    }


    /**
     * 虚引用：
     *      1、虚引用必须和引用队列（ReferenceQueue）联合使用，
     *          虚引用java.lang.ref.PhantomReference类来实现，与其他集中引用都不同，虚引用并不会决定对象的生命周期，
     *          如果一个对象仅持有虚引用，那么他就和没有任何引用一样，在任何时候都可以被垃圾回收器回收，它不能单独使用也不能
     *          通过它访问对象，虚引用必须和应用队列（ReferenceQueue）联合使用
     *      2、PhantomReference的get方法总是返回null
     *          虚引用的主要作用是跟踪对象被垃圾回收的状态，仅仅提供了一种对象被finalize以后，做某些事情的通知机制
     *          PhantomReference的get方法总是返回null,因此无法访问对应的引用对象
     *     3、处理监控通知使用：
     *          换句话说，设置虚引用关联对象的唯一目的，就是这个对象别收集器回收的时候收到一个系统通知或者后续添加进一步的处理，
     *          用来实现比finalize机制更加灵活的操作
     */
    public static void phantomReferenceDemo() {
        MyObject myObject = new MyObject();
        ReferenceQueue<MyObject> referenceQueue = new ReferenceQueue<>();
        PhantomReference<MyObject> phantomReference = new PhantomReference<>(myObject,referenceQueue);
//        System.out.println(phantomReference.get());
        List<byte[]> list = new ArrayList<>();
        new Thread(() -> {
            while (true){
                list.add(new byte[1 * 1024 * 1024]);
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(phantomReference.get() + " list add ok");
            }
        },"t1").start();

        new Thread(() -> {
            while (true){
                Reference<? extends MyObject> poll = referenceQueue.poll();
                if (poll != null){    //有虚引用对象被回收，加入了队列
                    System.out.println("Objects with phantom references have been reclaimed and added to the queue");
                    break;
                }
            }
        },"t2").start();
    }


    /**
     * 弱引用
     *      弱引用需要用java.lang.ref.WeakReference类来实现，它比软引用生存期更短
     *      对于只有弱引用的对象来说，只要垃圾回收机制一运行，不管JVM内存空间是否足够，都会回收该对象占用的内存
     */
    public static void weakReference() {
        WeakReference<MyObject> weakReference = new WeakReference<>(new MyObject());
//        System.out.println(weakReference.get());
        System.gc();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("the memory is enough : " + weakReference.get());
    }


    /**
     * 软引用
     *      软引用是相对于强引用弱化了一些的引用，需要用java.lang.ref.SoftReference类来实现，可以让对象豁免一些垃圾收集
     *      对于一个弱引用对象来说：
     *          当系统内存充足时     不会被回收
     *          当系统内存不足时      会被回收
     *      软引用通常用在对内存敏感的程序中，比如高速缓存就有用到软引用，内存够用就保留，不够就回收
     */
    public static void softReference() {
        SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
//        System.out.println("softReference: " + softReference.get());
        System.gc();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //需要修改虚拟机的配置参数  -Xms10m -Xmx10m
        System.out.println("gc after ,the memory is enough " + softReference.get());

        try {
            byte[] bytes = new byte[20 * 1024 * 1024];
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("gc after ,the memory is not enough " + softReference.get());
        }
    }

    /**
     * 强引用案例
     *      当内存不足时，JVM开始垃圾回收，对于强引用对象，就算是出现了OOM也不会对该对象进行回收
     *      强引用是最常见的普通对象引用，只要还有强引用指向一个对象，就能表明对象还“活着”，垃圾收集器不会碰这种对象
     *      在JAVA中，最常见的就是强引用，把一个对象赋给一个引用变量，这个引用变量就是一个强引用
     *      当一个对象被一个强引用变量引用时，它处于可达状态，它是不可能被垃圾回收机制进行回收的
     *      即使该对象以后永远都不会被用到，JVM也不会回收，因此强引用是造成JAVA内存泄露的主要原因之一
     *      对于一个普通对象，如果没有其他的引用关系，只要超过了引用的作用域或者显示的将相应（强）引用赋值为null
     */
    public static void strongReference() {
        MyObject myObject = new MyObject();
        System.out.println("gc before: " + myObject);  //会调用finalize
        myObject = null;
        System.gc();  //主动触发gc（垃圾回收机制   一般不用）
        System.out.println("gc after: " + myObject);
    }

}


class MyObject {
    
    //这个方法一般不重写
    @Override
    protected void finalize() throws Throwable {
        //finalize的通常目的是：在对象被不可撤销的丢弃之前执行清理操作
        System.out.println("invoke finalize method!!!");
    }
}